/* Copyright (c) 2010 OFXKit
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

#import "OFXObject.h"

static const char *getPropertyType(objc_property_t property) {
  const char *attributes = property_getAttributes(property);
  char buffer[1 + strlen(attributes)];
  strcpy(buffer, attributes);
  char *state = buffer, *attribute;
  while ((attribute = strsep(&state, ",")) != NULL) {
    if (attribute[0] == 'T') {
      return (const char *)[[NSData dataWithBytes:attribute length:strlen(attribute)] bytes];
    }
  }
  return "@";
}

@implementation OFXObject

@synthesize defaultOFXDateFormat;

+ (NSString*)stringWithUUID {
  CFUUIDRef	uuidObj = CFUUIDCreate(nil);//create a new UUID
  //get the string representation of the UUID
  NSString	*uuidString = (__bridge NSString *) CFUUIDCreateString(nil, uuidObj);
  CFRelease(uuidObj);
  return uuidString;
}

- (id)init
{
  self = [super init];
  self.defaultOFXDateFormat = [[NSDateFormatter alloc] init];
  // set the default date to exclude the timezone (parse as GMT) 
  // because some ofx providers have trouble with certain time zones.
  [defaultOFXDateFormat setDateFormat:@"yyyyMMddHHmmss.SSS"];
  [defaultOFXDateFormat setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
  return self;
}

- (NSString*)description
{
  NSString* parentDescription = [super description];
  NSString* myDescription = [NSString stringWithFormat:@"%@:", parentDescription];
  
  unsigned int outCount, i;
  Class myClass = [self class];
  
  while (myClass != [OFXObject class]) {
    objc_property_t *properties = class_copyPropertyList(myClass, &outCount);
    for(i = 0; i < outCount; i++) {
      objc_property_t property = properties[i];
      
      NSString* var = [NSString stringWithCString:property_getName(property) 
                                         encoding:[NSString defaultCStringEncoding]];
      NSString* propertyType = [NSString stringWithCString:getPropertyType(property)
                                                  encoding:[NSString defaultCStringEncoding]];
      
      if ([propertyType isEqual:@"TB"]) {
        myDescription = [NSString stringWithFormat:@" %@ %@ = (bool) %@,\r", myDescription,
                         var, [self valueForKey:var]];
      } else {
        myDescription = [NSString stringWithFormat:@" %@ %@ = %@,\r", myDescription,
                         var, [self valueForKey:var]];
      }
    }
    
    free(properties);
    myClass = [myClass superclass];      
  }
  
  return myDescription;
}

- (BOOL)isEqual:(id)object
{
  if ([object isKindOfClass:[self class]]) {
    BOOL objectIsEqual = YES;
    
    unsigned int outCount, i;
    Class myClass = [self class];
    
    while (myClass != [OFXObject class]) {
      objc_property_t *properties = class_copyPropertyList(myClass, &outCount);
      for(i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];

        NSString* var = [NSString stringWithCString:property_getName(property) 
                                           encoding:[NSString defaultCStringEncoding]];
  
        NSString* propertyType = [NSString stringWithCString:getPropertyType(property)
                                                    encoding:[NSString defaultCStringEncoding]];
        
        if ([self valueForKey:var] == nil || [propertyType isEqual:@"TB"]) {
          objectIsEqual &= [self valueForKey:var] == [object valueForKey:var];
        } else {
          objectIsEqual &= [[self valueForKey:var] isEqual:[object valueForKey:var]];
        }
      }

      free(properties);
      myClass = [myClass superclass];      
    }
    
    return objectIsEqual;
  }  
  
  return NO;   
}

- (NSUInteger)hash
{
  NSUInteger myHash = 0;

  unsigned int outCount, i;
  objc_property_t *properties = class_copyPropertyList([self class], &outCount);
  for(i = 0; i < outCount; i++) {
    objc_property_t property = properties[i];
    NSString* propertyType = [NSString stringWithCString:getPropertyType(property)
                                                encoding:[NSString defaultCStringEncoding]];
    
    if ([propertyType isEqual:@"TB"]) {
      NSString* var = [NSString stringWithCString:property_getName(property) 
                                         encoding:[NSString defaultCStringEncoding]];
    
      if ([self valueForKey:var] != nil) {
        myHash ^= [[self valueForKey:var] hash];
      }
    }
  }
  free(properties);
  return myHash;
}

@end

